/*
 * =====================================================================================
 *
 *       Filename:  cl_socket.cxx
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  03/24/2011 03:03:52 AM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Chen Ken (Ken), zlchen.ken@gmail.com
 *        Company:  EMC
 *
 * =====================================================================================
 */

#include <cstring>
#include <cassert>
#include <sys/types.h>
#include "cl_socket.hxx" 
// if cl_socket.h is included after socket_prototypes.hxx, curious error.
#include "cl_socket_prototypes.hxx"
#include "cl_socket_addr_factory.hxx"
#include "cl_socket_addr_in4.hxx"
#include "cl_buffer.hxx"

namespace neo
{
using namespace std;

CLSocket::CLSocket(const string& host, int port, CLSocketType type) 
  : m_sockAddr(CLSocketAddrFactory::create(host, port, type)),
  m_readerBuf(new CLBuffer),
  m_writerBuf(new CLBuffer),
  m_host(host),
  m_port(port) 
{
  m_socketfd = clSocket(m_sockAddr->family(), clSocketTypeToNativeType(type), 0);
}

CLSocket::CLSocket(int socketfd, CLSocketAddrUPtr addr)
  : m_sockAddr(move(addr)),
  m_readerBuf(new CLBuffer),
  m_writerBuf(new CLBuffer),
  m_host(""),
  m_port(-1),
  m_socketfd(socketfd)
{
}

CLSocket::~CLSocket() 
{
  if(isValid())
  {
  	::close(m_socketfd);
  }
}

ssize_t CLSocket::send(const void* data, size_t siz) 
{
  assert(isValid());
  return m_writerBuf->writefd(m_socketfd, data, siz);
}

ssize_t CLSocket::sendOutLeft(void)
{
  assert(isValid());
  return m_writerBuf->writefd(m_socketfd);
}

ssize_t CLSocket::receive(void) 
{
  assert(isValid());
  return m_readerBuf->readfd(m_socketfd);
}

bool CLSocket::hasDataToSend(void) const 
{
  return (m_writerBuf->readableBytes() != 0);
}

bool CLSocket::shutDown(CLConnCloseType type)
{
  assert(isValid());
  m_readerBuf->reset();
  m_writerBuf->reset();
  if(clShutdown(m_socketfd, clConnCloseTypeToNativeType(type)) < 0)
  {
    return false;
  }
  return true;
}

bool CLSocket::setBlock(bool isBlock) 
{
  assert(isValid());
  if(clSetFdBlocking(m_socketfd, isBlock) < 0)
  {
    return false;
  }
  return true;
}

bool CLSocket::setReuseable(bool isReuseable) 
{
  assert(isValid());
  int on = 1;
  if(!isReuseable)
  {
    on = 0;
  }

  if(clSetsockopt(m_socketfd, SOL_SOCKET, SO_REUSEADDR, (char*)(&on), sizeof(on)) < 0) 
  {
    return false;
  }
  return true;
}

bool CLSocket::isValid(void) const 
{
  return (m_socketfd >= 0);
}

int CLSocket::fd(void) const 
{
  return m_socketfd;
}

CLBuffer& CLSocket::readerBuf(void) const
{
  return *m_readerBuf;
}

CLBuffer& CLSocket::writerBuf(void) const
{
  return *m_writerBuf;
}

int CLSocket::lastErrorCode(void)
{
  assert(isValid());
  int opval;
	socklen_t oplen = sizeof(opval);
  if(clGetsockopt(m_socketfd, SOL_SOCKET, SO_ERROR, &opval, &oplen) < 0)
	{
	  return errno;
  }
	else
	{
		return opval;
	}
}

} // neo

